﻿using System;
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;

using Object = UnityEngine.Object;

class ResourceManager
{
    static ResourceManager _instance;
    public static ResourceManager Instance
    {
        get
        {
            if (_instance == null)
                _instance = new ResourceManager();

            return _instance;
        }
    }

    #region 异步加载
    /// <summary>
    /// 加载资源, 回调返回资源实例引用;
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="path">资源路径</param>
    /// <param name="loaded">回调返回资源引用</param>
    /// <returns>资源实例, 用于释放</returns>
    public ResourceItem LoadAsset<T>(string path, Action<T> loaded) where T : Object
    {
        var resItem = new ResourceItem(path);
        CoroutineHelper.Instance.Begin(LoadAssetCortine(resItem, loaded));
        return resItem;
    }

    IEnumerator LoadAssetCortine<T>(ResourceItem item, Action<T> loaded) where T : Object
    {
        item.request = Resources.LoadAsync(item.loadPath);
        yield return item.request;
        item.asset = item.request.asset;
        item.loadCortine.IsDone = true;
        if (item.asset != null && item.asset is T)
        {
            if (loaded != null)
                loaded.Invoke(item.asset as T);
        }
        else
            Debug.LogWarning("load asset failure:" + item.loadPath);
    }

    /// <summary>
    /// 加载资源, 回调返回实例化的Object;
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="path">资源路径</param>
    /// <param name="loaded">回调返回实例</param>
    /// <returns>资源实例, 用于释放</returns>
    public ResourceItem LoadObject<T>(string path, Action<T> loaded) where T : Object
    {
        var resItem = new ResourceItem(path);
        CoroutineHelper.Instance.Begin(LoadResCortine(resItem, loaded));
        return resItem;
    }

    IEnumerator LoadResCortine<T>(ResourceItem item, Action<T> loaded) where T : Object
    {
        item.request = Resources.LoadAsync<T>(item.loadPath);
        yield return item.request;
        item.asset = item.request.asset;
        var instance = item.Instantiate<T>();
        item.loadCortine.IsDone = true;
        if (item.asset != null && item.asset is T)
        {
            if (!item.disposed && loaded != null)
                loaded.Invoke(instance);
        }
        else
            Debug.LogWarning("load failure:" + item.loadPath);
    }

    public ResourceItem LoadScene(string sceneName, Action onLoaded)
    {
        var syc = SceneManager.LoadSceneAsync(sceneName);
        var resItem = new ResourceItem(syc);
        CoroutineHelper.Instance.Begin(LoadSceneCortine(resItem, onLoaded));
        return resItem;
    }
    IEnumerator LoadSceneCortine(ResourceItem resItem, Action onloaded)
    {
        yield return resItem.sceneOp;
        resItem.loadCortine.IsDone = true;
        if (onloaded != null)
            onloaded.Invoke();
    }

    public ResourceItem LoadBundle<T>(string bundleName, string assetName, Action<T> loaded) where T : Object
    {
        var BundleURL =
#if UNITY_STANDALONE_WIN || UNITY_EDITOR
        "file://" + Application.dataPath + "/StreamingAssets/";
#elif UNITY_IPHONE
        Application.dataPath + "/Raw/StreamingAssets/";
#elif UNITY_ANDROID
        "jar:file://" + Application.dataPath + "!/assets/";
#else
        string.Empty;
#endif
        var url = BundleURL + bundleName;
        var resItem = new ResourceItem(url, assetName);
        CoroutineHelper.Instance.Begin(LoadBundleCortine(resItem, loaded));
        return resItem;
    }
    IEnumerator LoadBundleCortine<T>(ResourceItem resItem, Action<T> loaded) where T : Object
    {
        yield return resItem.www;
        if (string.IsNullOrEmpty(resItem.www.error))
        {
            var bundle = resItem.www.assetBundle;
            var request = bundle.LoadAssetAsync<T>(resItem.assetName);
            yield return request;
            resItem.asset = request.asset;
            bundle.Unload(false);
            bundle = null;
            resItem.www.Dispose();
            var instance = resItem.Instantiate<T>();
            resItem.loadCortine.IsDone = true;
            if (instance != null)
            {
                if (!resItem.disposed && loaded != null)
                    loaded.Invoke(instance);
            }
            else
                Debug.LogWarning("load failure:" + resItem.loadPath);
        }
        else
            Debug.LogWarning("www load failure:" + resItem.loadPath);
    }
    #endregion

    #region 同步加载
    /// <summary>
    /// 加载资源, 返回资源的引用, 适用于不需要实例化的资源;
    /// </summary>
    /// <param name="prefabPath">路径</param>
    /// <returns>资源引用</returns>
    public T LoadAssetSync<T>(string prefabPath) where T : Object
    {
        return Resources.Load<T>(prefabPath);
    }

    /// <summary>
    /// 加载资源, 返回资源的引用, 适用于不需要实例化的资源;
    /// </summary>
    /// <param name="prefabPath">路径</param>
    /// <returns>资源引用</returns>
    public T[] LoadAllAssetSync<T>(string prefabPath) where T : Object
    {
        return Resources.LoadAll<T>(prefabPath);
    }

    /// <summary>
    /// 同步加载Object, 并实例化
    /// </summary>
    /// <param name="prefabPath">路径</param>
    /// <returns>资源实例</returns>
    public T LoadObjectSync<T>(string prefabPath) where T : Object
    {
        var prefab = Resources.Load(prefabPath);
        if (prefab is T)
        {
            return Object.Instantiate<T>(prefab as T);
        }
        else
        {
            Debug.LogWarningFormat("asset:{0} not match TYPE", prefabPath);
            return null;
        }
    }

    /// <summary>
    /// 同步加载GameObject, 并实例化
    /// </summary>
    /// <param name="prefabPath">路径</param>
    /// <returns>资源实例</returns>
    public GameObject LoadGameObjectSync(string prefabPath)
    {
        var gameobject = LoadObjectSync<GameObject>(prefabPath);
        return gameobject;
    }
    #endregion
}
